/*
 *  Copyright (c) 2018, Infineon Technologies AG
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification,are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holders nor the names of its contributors
 *  may be used to endorse or promote products derived from this software without
 *  specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  To improve the quality of the software, users are encouraged to share
 *  modifications, enhancements or bug fixes with Infineon Technologies AG
 *  dave@infineon.com).
 */
/**
 * \defgroup analog_to_digital Analog to digital
 * @{
 */
/**
 * \file	config_adc.c
 * \author 	Manuel Escudero Rodriguez
 * \date	09.05.2018
 * \brief   ADC configuration
 */
#include <DAVE.h>
#include "xmc4200.h"
#include "xmc4_scu.h"
#include "config_adc.h"
#include "controller.h"

/**
 * \name Global ADC configuration
 * @{
 */
/** Initialization data of VADC global resources */
const XMC_VADC_GLOBAL_CONFIG_t adc_global_config =
{
    .disable_sleep_mode_control = true,
    .module_disable = false,
    .class0 =
    {
        .conversion_mode_standard = XMC_VADC_CONVMODE_12BIT,
        .sample_time_std_conv = 0U,
        .conversion_mode_emux = XMC_VADC_CONVMODE_12BIT,
        .sampling_phase_emux_channel = 0U
    },
    .class1 =
    {
        .conversion_mode_standard = XMC_VADC_CONVMODE_12BIT,
        .sample_time_std_conv = 0U,
        .conversion_mode_emux = XMC_VADC_CONVMODE_12BIT,
        .sampling_phase_emux_channel = 0U
    },
    .clock_config =
    {
        .analog_clock_divider = 0, /* Clock for the converter. */
        .msb_conversion_clock = 0, /* Additional clock cycle for analog converter. */
        .arbiter_clock_divider = 0 /* Request source arbiter clock divider. */
    },
};
/** @} */

/**
 * \name Group ADC configuration
 * @{
 */
/** Initialization data of a VADC group 0 */
const XMC_VADC_GROUP_CONFIG_t adc_group_config_0 =
{
    .class0 =
    {
        .conversion_mode_standard = XMC_VADC_CONVMODE_12BIT,
        .sample_time_std_conv = 4U /* Additional cycles at sample time. */
    },
    .class1 =
    {
       .conversion_mode_standard = XMC_VADC_CONVMODE_10BIT,
       .sample_time_std_conv = 4U /* Additional cycles at sample time. */
    }
};
/** Initialization data of a VADC group 1*/
const XMC_VADC_GROUP_CONFIG_t adc_group_config_1 =
{
    .class0 =
    {
        .conversion_mode_standard = XMC_VADC_CONVMODE_10BIT,
        .sample_time_std_conv = 0U /* Additional cycles at sample time. */
    },
    .class1 =
    {
       .conversion_mode_standard = XMC_VADC_CONVMODE_FASTCOMPARE,
       .sample_time_std_conv = 0U /* Additional cycles at sample time. */
    }
};
/** @} */

/**
 * \name Result ADC configuration
 * @{
 */
/** Result configuration data of input current */
const XMC_VADC_RESULT_CONFIG_t adc_res_il_handle =
{
    .post_processing_mode = XMC_VADC_DMM_FILTERING_MODE,
    .data_reduction_control = 0x3, /* Filter selection */
    .part_of_fifo = 0, /* No FIFO */
    .wait_for_read_mode = (bool) false,
    .event_gen_enable = (bool) false /* result event */
};
/** Result configuration data of input current */
const XMC_VADC_RESULT_CONFIG_t adc_res_io_handle =
{
    .post_processing_mode = XMC_VADC_DMM_REDUCTION_MODE,
    .data_reduction_control = 0, /* Accumulation selection disabled */
    .part_of_fifo = 0, /* No FIFO */
    .wait_for_read_mode = (bool) false,
    .event_gen_enable = (bool) false /* result event */
};
/** Result configuration data of output voltage */
const XMC_VADC_RESULT_CONFIG_t adc_res_vo_handle =
{
    .post_processing_mode = XMC_VADC_DMM_FILTERING_MODE,
    .data_reduction_control = 0xE, /* Filter selection */
    .part_of_fifo = 0, /* No FIFO */
    .wait_for_read_mode = (bool) false,
    .event_gen_enable = (bool) false /* result event */
};
/** Result configuration data of under voltage protection */
const XMC_VADC_RESULT_CONFIG_t adc_res_uvp_handle =
{
    .post_processing_mode = XMC_VADC_DMM_REDUCTION_MODE,
    .data_reduction_control = 0, /* Normal operation */
    .part_of_fifo = 0, /* No FIFO */
    .wait_for_read_mode = (bool) false,
    .event_gen_enable = (bool) false /* result event */
};
/** Result configuration data of input voltage */
const XMC_VADC_RESULT_CONFIG_t adc_res_vin_handle =
{
    .post_processing_mode = XMC_VADC_DMM_FILTERING_MODE,
    .data_reduction_control = 0x3, /* Filter selection */
    .part_of_fifo = 0, /* No FIFO */
    .wait_for_read_mode = (bool) false,
    .event_gen_enable = (bool) false /* result event */
};
/** Result configuration data of temperature */
const XMC_VADC_RESULT_CONFIG_t adc_res_ntc_handle =
{
    .post_processing_mode = XMC_VADC_DMM_FILTERING_MODE,
    .data_reduction_control = 0x3, /* Filter selection */
    .part_of_fifo = 0, /* No FIFO */
    .wait_for_read_mode = (bool) false,
    .event_gen_enable = (bool) false /* result event */
};
/** @} */

/**
 * \name Queue ADC configuration
 * @{
 */
/** Queue g0 configuration */
const XMC_VADC_QUEUE_CONFIG_t adc_queue_g0_config =
{
    .conv_start_mode = XMC_VADC_STARTMODE_CNR,
    .req_src_priority = XMC_VADC_GROUP_RS_PRIORITY_3, /* Highest priority for the queue in the arbitration round */
    .src_specific_result_reg = 0x0, /* Individual configured result registers used */
    .trigger_signal = XMC_VADC_REQ_TR_I, /* CCU80.SR2 as trigger for the queue */
    .trigger_edge = XMC_VADC_TRIGGER_EDGE_RISING,
    .external_trigger = true
};
/** Queue g1 configuration */
const XMC_VADC_QUEUE_CONFIG_t adc_queue_g1_config =
{
    .conv_start_mode = XMC_VADC_STARTMODE_CNR,
    .req_src_priority = XMC_VADC_GROUP_RS_PRIORITY_3, /* Highest priority for the queue in the arbitration round */
    .src_specific_result_reg = 0x0, /* Individual configured result registers used */
    .trigger_signal = XMC_VADC_REQ_TR_I, /* CCU80.SR2 as trigger for the queue */
    .trigger_edge = XMC_VADC_TRIGGER_EDGE_RISING,
    .external_trigger = true
};
/** First queue entry for NTC measurement */
const XMC_VADC_QUEUE_ENTRY_t adc_queue_ntc_entry_0 =
{
    .channel_num = NTC_CH,
    .refill_needed = true,
    .generate_interrupt = false,
    .external_trigger = true
};
/** First queue entry for input voltage measurement */
const XMC_VADC_QUEUE_ENTRY_t adc_queue_vin_entry_0 =
{
    .channel_num = VIN_CH,
    .refill_needed = true,
    .generate_interrupt = false,
    .external_trigger = false
};
/** First queue entry for current measurement */
const XMC_VADC_QUEUE_ENTRY_t adc_queue_il_entry_0 =
{
    .channel_num = IL_CH,
    .refill_needed = true,
    .generate_interrupt = false,
    .external_trigger = true
};
/** Second queue entry for current measurement */
const XMC_VADC_QUEUE_ENTRY_t adc_queue_il_entry_1 =
{
    .channel_num = IL_CH,
    .refill_needed = true,
    .generate_interrupt = false,
    .external_trigger = true
};
/** First queue entry for current measurement */
const XMC_VADC_QUEUE_ENTRY_t adc_queue_io_entry_0 =
{
    .channel_num = IO_CH,
    .refill_needed = true,
    .generate_interrupt = false,
    .external_trigger = false
};
/** First queue entry for current measurement */
const XMC_VADC_QUEUE_ENTRY_t adc_queue_io_entry_1 =
{
    .channel_num = IO_CH,
    .refill_needed = true,
    .generate_interrupt = false,
    .external_trigger = false
};
/** First queue entry for current measurement */
const XMC_VADC_QUEUE_ENTRY_t adc_queue_vo_entry_0 =
{
    .channel_num = VO_CH,
    .refill_needed = true,
    .generate_interrupt = true,
    .external_trigger = false
};
/** First queue entry for current measurement */
const XMC_VADC_QUEUE_ENTRY_t adc_queue_vo_entry_1 =
{
    .channel_num = VO_CH,
    .refill_needed = true,
    .generate_interrupt = true,
    .external_trigger = false
};
/** @} */

/**
 * \name Channel ADC configuration
 * @{
 */
/** Channel configuration of input current */
XMC_VADC_CHANNEL_CONFIG_t adc_ch_il_config =
{
    .input_class = XMC_VADC_CHANNEL_CONV_GROUP_CLASS1,
    .result_reg_number = IL_RES,
    .channel_priority = true,
    .alias_channel = XMC_VADC_CHANNEL_ALIAS_DISABLED,
	.alternate_reference = true
};
/** Channel configuration of output current */
XMC_VADC_CHANNEL_CONFIG_t adc_ch_io_config =
{
    .input_class = XMC_VADC_CHANNEL_CONV_GROUP_CLASS0,
    .result_reg_number = IO_RES,
    .channel_priority = true,
    .alias_channel = XMC_VADC_CHANNEL_ALIAS_DISABLED,
	.alternate_reference = true
};
XMC_VADC_CHANNEL_CONFIG_t adc_ch_vo_config =
{
    .input_class = XMC_VADC_CHANNEL_CONV_GROUP_CLASS0,
    .result_reg_number = VO_RES,
    .channel_priority = true,
    .alias_channel = XMC_VADC_CHANNEL_ALIAS_DISABLED,
    .upper_boundary_select = 0x1,
    .lower_boundary_select = 0x0,
	.alternate_reference = true
};
XMC_VADC_CHANNEL_CONFIG_t adc_ch_uvp_config =
{
    .input_class = XMC_VADC_CHANNEL_CONV_GROUP_CLASS1,
    .result_reg_number = UVP_RES,
    .channel_priority = true,
    .alias_channel = XMC_VADC_CHANNEL_ALIAS_DISABLED,
	.alternate_reference = true
};
XMC_VADC_CHANNEL_CONFIG_t adc_ch_vin_config =
{
    .input_class = XMC_VADC_CHANNEL_CONV_GROUP_CLASS0,
    .result_reg_number = VIN_RES,
    .channel_priority = true,
    .alias_channel = XMC_VADC_CHANNEL_ALIAS_DISABLED,
    .upper_boundary_select = 0x1,
    .lower_boundary_select = 0x0,
	.alternate_reference = true
};
XMC_VADC_CHANNEL_CONFIG_t adc_ch_ntc_config =
{
    .input_class = XMC_VADC_CHANNEL_CONV_GROUP_CLASS0,
    .result_reg_number = NTC_RES,
    .channel_priority = true,
    .alias_channel = XMC_VADC_CHANNEL_ALIAS_DISABLED,
	.alternate_reference = true
};
/** @} */

/**
 * \name Scan ADC configuration
 * @{
 */
/* Scan configuration */
const XMC_VADC_SCAN_CONFIG_t adc_scan_g1_config =
{
    .conv_start_mode = XMC_VADC_STARTMODE_WFS, /* Wait for start mode */
    .req_src_priority = XMC_VADC_GROUP_RS_PRIORITY_3, /* Second highest priority for the scan in the arbitration round */
    .src_specific_result_reg = 0x0,  /* Individual configured result registers used */
    .trigger_signal = XMC_VADC_REQ_TR_A,
    .trigger_edge = XMC_VADC_TRIGGER_EDGE_NONE, /* Trigger by software */
    .external_trigger = 0 /* Not enable external trigger */
};
/** @} */

/*---------------------------------------------------------------------------*/
/**
 * \brief   Configuration and initialization of the die temperature sensor.<BR>
 * \return  None
 */
/* Function to initialize the ADC module */
void die_temp_sens_init(void)
{
	XMC_SCU_EnableTemperatureSensor();
	XMC_SCU_CalibrateTemperatureSensor(0, 0);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief   Configuration and initialization of the die temperature sensor.<BR>
 * \return  None
 */
/* Function to initialize the ADC module */
#ifndef DOXYGEN_SKIP
__attribute__((section(".ram_code")))
#endif
inline uint16_t die_temp_sens_value(void)
{
	uint16_t die_temperature;
	/* If the die temperature sensor is not busy. */
	if (!(SCU_GENERAL->DTSSTAT & SCU_GENERAL_DTSSTAT_BUSY_Msk))
	{
		/* Start a new temperature measurement. */
		SCU_GENERAL->DTSCON |= (uint32_t)SCU_GENERAL_DTSCON_START_Msk;
	}
	die_temperature = (uint16_t)((SCU_GENERAL->DTSSTAT & SCU_GENERAL_DTSSTAT_RESULT_Msk) >> SCU_GENERAL_DTSSTAT_RESULT_Pos);
	return die_temperature;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief   Configuration and initialization of the ADC peripheral.<BR>
 *          Global, group, channels and results are configured.
 *          Queue is configured and initialized.
 *          Pointer to the ADC measurements are defined.
 * \return  None
 */
/* Function to initialize the ADC module */
void adc_init(void)
{
    /* Initialization of the ADC peripheral */
    XMC_SCU_CLOCK_UngatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_VADC);
    XMC_VADC_GLOBAL_Init(VADC, &adc_global_config);
    XMC_VADC_GROUP_Init(VADC_G0, &adc_group_config_0);
    XMC_VADC_GROUP_Init(VADC_G1, &adc_group_config_1);

    /* Configuration of the channels to be measured */
    XMC_VADC_GROUP_ChannelInit(ADC_IL_CH, &adc_ch_il_config);
    XMC_VADC_GROUP_ChannelInit(ADC_IO_CH, &adc_ch_io_config);
    XMC_VADC_GROUP_ChannelInit(ADC_VO_CH, &adc_ch_vo_config);
    XMC_VADC_GROUP_ChannelInit(ADC_UVP_CH, &adc_ch_uvp_config);
    XMC_VADC_GROUP_ChannelInit(ADC_VIN_CH, &adc_ch_vin_config);
    XMC_VADC_GROUP_ChannelInit(ADC_NTC_CH, &adc_ch_ntc_config);

    /* Result registers initialization */
    XMC_VADC_GROUP_ResultInit(ADC_IL_RES, &adc_res_il_handle);
    XMC_VADC_GROUP_ResultInit(ADC_IO_RES, &adc_res_io_handle);
    XMC_VADC_GROUP_ResultInit(ADC_VO_RES, &adc_res_vo_handle);
    XMC_VADC_GROUP_ResultInit(ADC_UVP_RES, &adc_res_uvp_handle);
    XMC_VADC_GROUP_ResultInit(ADC_VIN_RES, &adc_res_vin_handle);
    XMC_VADC_GROUP_ResultInit(ADC_NTC_RES, &adc_res_ntc_handle);

    /* Initialization of the queue G0 */
    XMC_VADC_GROUP_QueueInit(VADC_G0, &adc_queue_g0_config);
    XMC_VADC_GROUP_QueueInsertChannel(VADC_G0, adc_queue_il_entry_0);
    XMC_VADC_GROUP_QueueInsertChannel(VADC_G0, adc_queue_vo_entry_0);
    XMC_VADC_GROUP_QueueInsertChannel(VADC_G0, adc_queue_io_entry_0);
    XMC_VADC_GROUP_QueueInsertChannel(VADC_G0, adc_queue_il_entry_1);
    XMC_VADC_GROUP_QueueInsertChannel(VADC_G0, adc_queue_vo_entry_1);
    XMC_VADC_GROUP_QueueInsertChannel(VADC_G0, adc_queue_io_entry_1);
    /* Connect Request Source Event to the NVIC nodes */
    XMC_VADC_GROUP_QueueSetReqSrcEventInterruptNode(VADC_G0, XMC_VADC_SR_GROUP_SR1);
    /*
     * Assign the second highest priority to the compensator loop.
     * Higher priority for the latching protections.
     */
    NVIC_SetPriority(HIGH_FRQ_IRQ_N, 1);
    
    /* Initialization of the queue G0 */
    XMC_VADC_GROUP_QueueInit(VADC_G1, &adc_queue_g1_config);
    XMC_VADC_GROUP_QueueInsertChannel(VADC_G1, adc_queue_ntc_entry_0);
    XMC_VADC_GROUP_QueueInsertChannel(VADC_G1, adc_queue_vin_entry_0);

    /* Configure output voltage measurement out of range protection. */
    XMC_VADC_GROUP_SetBoundaries(VADC_G0, 0, VOLTAGE_OVP);
    /* Connect Request Source Event to the NVIC node of the protection interrupt */
    XMC_VADC_GROUP_ChannelTriggerEventGenCriteria(ADC_VO_CH, XMC_VADC_CHANNEL_EVGEN_OUTBOUND);
    XMC_VADC_GROUP_ChannelSetEventInterruptNode(ADC_VO_CH, XMC_VADC_SR_GROUP_SR2);
    /* Out of range output voltage has the highest priority, is a latching protection. */
    NVIC_SetPriority(OVP_IRQ_N, 0);
    
    /* Configure output voltage measurement out of range protection. */
	XMC_VADC_GROUP_SetBoundaries(VADC_G1, 0, BOOST_VOLTAGE_OVP);
	/* Connect Request Source Event to the NVIC node of the protection interrupt */
	XMC_VADC_GROUP_ChannelTriggerEventGenCriteria(ADC_VIN_CH, XMC_VADC_CHANNEL_EVGEN_OUTBOUND);
	XMC_VADC_GROUP_ChannelSetEventInterruptNode(ADC_VIN_CH, XMC_VADC_SR_GROUP_SR2);
	/* Out of range output voltage has the highest priority, is a latching protection. */
	NVIC_SetPriority(BOOST_OVP_IRQ_N, 0);

    /** \todo Move Vin and NTC measurements to a background source. Improve UVP reaction time. */
    /* Scan source for measuring input voltage */
    XMC_VADC_GROUP_ScanInit(VADC_G1, &adc_scan_g1_config);
    XMC_VADC_GROUP_ScanEnableContinuousMode(VADC_G1);
//    XMC_VADC_GROUP_ScanAddChannelToSequence(ADC_VIN_CH);

    /* Scan source for measuring temperature from an NTC sensor */
//    XMC_VADC_GROUP_ScanAddChannelToSequence(ADC_NTC_CH);

    /* Scan source for measuring Under Voltage Protection-Fast Short Circuit Protection */
    XMC_VADC_GROUP_ScanAddChannelToSequence(ADC_UVP_CH);
    XMC_VADC_GROUP_SetResultFastCompareValue(ADC_UVP_RES, VOLTAGE_UVP);
    /* Connect Request Source Event to the NVIC nodes */
    XMC_VADC_GROUP_ChannelTriggerEventGenCriteria(ADC_UVP_CH, XMC_VADC_CHANNEL_EVGEN_COMPLOW);
    XMC_VADC_GROUP_ChannelSetEventInterruptNode(ADC_UVP_CH, XMC_VADC_SR_GROUP_SR1);
    /* Additional under voltage protection. Redundant with the out of range protection. */
    NVIC_SetPriority(UVP_IRQ_N, 0);

    /* Enable the analog to digital converters power supply */
    XMC_VADC_GROUP_SetPowerMode(VADC_G0, XMC_VADC_GROUP_POWERMODE_NORMAL);
    XMC_VADC_GROUP_SetPowerMode(VADC_G1, XMC_VADC_GROUP_POWERMODE_NORMAL);
    /* Perform calibration of the converter */
    XMC_VADC_GLOBAL_StartupCalibration(VADC);

    /* Load event that starts measurements in scan mode. */
    VADC_G0->ASMR |= (1 << VADC_G_ASMR_LDEV_Pos) & VADC_G_ASMR_LDEV_Msk;
    VADC_G1->ASMR |= (1 << VADC_G_ASMR_LDEV_Pos) & VADC_G_ASMR_LDEV_Msk;
}
/**}@*/
